Table 2: Explanation of typedefs in inttypes.h.
Routines that take variable argument lists can often hide 64-bit portability problems. The most common examples of such routines are standard routines like print, scanf, fprintf, syslog, vprintf, and their relatives. In the case of printf-like routines, the variable number of arguments of various types are interpreted at run-time by a format string parameter. Routines using variable argument lists tend to obscure 64-bit portability problems for two related reasons:
#include <stdio.h> main(int argc, char **argv) { char *thirty = "30"; long value; sscanf(thirty, "%d", &value); printf("result = %d\n", value); }On a 32-bit system, the program outputs 30 as expected. On a 64-bit LP64 system, the program outputs an undefined value. The reason is because value is declared as a long but is printed out by the %d format specifier intended for int values. The program can be fixed by either changing the type of value to int or by changing the sscanf format string to be %ld to scan into a long value.
More 64-bit printf-related advice is to use the %p format specifier (defined in the ANSI C library specification) when printing pointers values (generally, this is limited to debugging purposes). Using the more traditional %x specifier to print pointers as hexadecimal integers will output a truncated representation of 64-bit pointers. Bogus debugging output can only make debugging your 64-bit code harder.
To demonstrate the second problem, consider a routine to print a null terminated list of character strings. Remember that a null pointer is represented by the integral value 0. The routine is implemented portably as:
#include <stdarg.h> void print_list(char *word, ...) { va_list ap; va_start(ap, word); while(word) { puts(word); word = va_arg(ap, char *); } va_end(ap); }But, problems can result when you call the routine in an unportable manner. For example:
print_list("hi", "there", 0);Trailing variable arguments beyond the explictly typed arguments of print_list suffer the default argument promotion rules. It is ambiguous whether the trailing zero should be passed as a pointer or an integer. If treated as an integer, default argument promotion rules would pass the 0 as an int which is not the size of a 64-bit LP64 pointer. The call to print_list could be portably rewritten:
print_list("hi", "there", (void*) 0);Using the standard NULL symbolic constant found in stdio.h or stdlib.h should also ensure portability.